package top.xtcoder.xtpsd.core.layermask;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import top.xtcoder.xtpsd.core.layermask.handle.LayerEffectHandle;
import top.xtcoder.xtpsd.core.layermask.lang.BlendMode;
import top.xtcoder.xtpsd.core.layermask.lang.EffectSet;
import top.xtcoder.xtpsd.core.layermask.vo.BlendModeInfo;
import top.xtcoder.xtpsd.core.layermask.vo.ChannelImageData;
import top.xtcoder.xtpsd.core.layermask.vo.ChannelInfoData;
import top.xtcoder.xtpsd.core.layermask.vo.ChannelInformation;
import top.xtcoder.xtpsd.core.layermask.vo.LayerBlendingRange;
import top.xtcoder.xtpsd.core.layermask.vo.LayerEffect;
import top.xtcoder.xtpsd.core.layermask.vo.LayerInfo;
import top.xtcoder.xtpsd.core.layermask.vo.LayerMaskData;
import top.xtcoder.xtpsd.core.layermask.vo.LayerNameInfo;
import top.xtcoder.xtpsd.core.layermask.vo.LayerRecord;
import top.xtcoder.xtpsd.core.layermask.vo.LayerRecordCoordinate;
import top.xtcoder.xtpsd.core.layermask.vo.LayerSetFlagInfo;
import top.xtcoder.xtpsd.core.layermask.vo.PsdLayerAndMaskInfomation;
import top.xtcoder.xtpsd.utils.ByteUtil;

public class PsdLayerAndMaskInfomationParse {
	private InputStream inputStream;
	
	public PsdLayerAndMaskInfomation parse(FileInputStream fis) throws IOException {
		PsdLayerAndMaskInfomation info = new PsdLayerAndMaskInfomation();
		int length = ByteUtil.readByteToInt(fis, 4);
		info.setLength(length);
		System.out.println("图层与蒙版信息数据长度" + length);
		
		byte[] bsContent = new byte[length];
		fis.read(bsContent);
		inputStream = new ByteArrayInputStream(bsContent);
		
		LayerInfo layerInfo = parseLayerInfo(info);
		info.setLayerInfo(layerInfo);
		return info;
	}
	
	/**
	 * 解析图层信息Layer info
	 * @param info
	 * @throws IOException
	 */
	private LayerInfo parseLayerInfo(PsdLayerAndMaskInfomation info) throws IOException {
		LayerInfo layerInfo = new LayerInfo();
		
		int layerInfoLength = ByteUtil.readByteToInt(inputStream, 4);
		layerInfo.setLayersLength(layerInfoLength);
		
		byte[] bsLayerCount = new byte[2];
		inputStream.read(bsLayerCount);
		int layerCount = ByteUtil.bytesToInt(bsLayerCount);
		layerInfo.setLayerCount(layerCount);
		if(layerCount <= 0) {
			return layerInfo;
		}
		System.out.println("图层数=" + layerInfo.getLayerCount());
		
		List<LayerRecord> records = new ArrayList<>();
		List<ChannelImageData> channelImageDatas = new ArrayList<>();
		for(int i = 0; i < layerInfo.getLayerCount(); i ++) {
			LayerRecord record = parseLayerRecords();
//			ChannelImageData channelImageData = parseChannelImageData(record);
//			channelImageDatas.add(channelImageData);
			records.add(record);
		}
//		for(int i = 0; i < layerInfo.getLayerCount(); i ++) {
//			LayerRecord record = parseLayerRecords();
//			ChannelImageData channelImageData = parseChannelImageData(records.get(i));
//			channelImageDatas.add(channelImageData);
//			records.add(record);
//		}
		layerInfo.setRecords(records);
		layerInfo.setImageDatas(channelImageDatas);
		return layerInfo;
	}
	
	/**
	 * 解析图层记录Layer records
	 * @throws IOException 
	 */
	private LayerRecord parseLayerRecords() throws IOException {
		LayerRecord record = new LayerRecord();
		
		LayerRecordCoordinate coordinate = new LayerRecordCoordinate();
		parseCoordinate(coordinate);
		record.setCoordinate(coordinate);
		
		ChannelInformation channelInformation = new ChannelInformation();
		parseChannelInformation(channelInformation);
		record.setChannelInformation(channelInformation);
		
		BlendModeInfo mode = new BlendModeInfo();
		parseBlendMode(mode);
		record.setBlendMode(mode);
		
		record.setOpacity(ByteUtil.readByteToInt(inputStream, 1));
		record.setClipping(ByteUtil.readByteToInt(inputStream, 1));
		
		LayerSetFlagInfo flag = new LayerSetFlagInfo();
		parseLayerSetFlags(flag);
		record.setFlag(flag);
		
		record.setFiller(ByteUtil.readByteToInt(inputStream, 1));
		record.setExtraDataLength(ByteUtil.readByteToInt(inputStream, 4));
		
		LayerMaskData maskData = new LayerMaskData();
		parseLayerMaskData(maskData);
		record.setMaskData(maskData);
		
		LayerBlendingRange blendingRange = new LayerBlendingRange();
		parseLayerBlendingRange(blendingRange);
		record.setBlendingRange(blendingRange);
		
		LayerNameInfo nameInfo = new LayerNameInfo();
		int readNameSize = parseLayerName(nameInfo);
		record.setNameInfo(nameInfo);
		System.out.println("layer.name=" + nameInfo.getName());
		int readEffectSize = parseLayerEffects(record);
		
		return record;
	}
	
	/**
	 * 解析图层的上下左右
	 * @return
	 * @throws IOException
	 */
	private int parseCoordinate(LayerRecordCoordinate coordinate) throws IOException {
		double top = ByteUtil.readByteToFloat4(inputStream);
		double left = ByteUtil.readByteToFloat4(inputStream);
		double bottom = ByteUtil.readByteToFloat4(inputStream);
		double right = ByteUtil.readByteToFloat4(inputStream);
		
		coordinate.setTop(top);
		coordinate.setLeft(left);
		coordinate.setBottom(bottom);
		coordinate.setRight(right);
		return 4 * 4;
	}
	
	private int parseChannelInformation(ChannelInformation info) throws IOException {
		int channelNumber = ByteUtil.readByteToInt(inputStream, 2);
		info.setChannelNumber(channelNumber);
		
		List<ChannelInfoData> channelDatas = new ArrayList<ChannelInfoData>();
		for(int i = 0; i < channelNumber; i++) {
			ChannelInfoData channelInfoData = new ChannelInfoData();
			
			int channelId = ByteUtil.readByteToInt(inputStream, 2);
			int channelDataLength = ByteUtil.readByteToInt(inputStream, 4);
			channelInfoData.setChannelId(channelId);
			channelInfoData.setChannelDataLength(channelDataLength);
			
			channelDatas.add(channelInfoData);
		}
		info.setChannelDatas(channelDatas);
		return 2 + channelNumber * 6;
	}
	
	private int parseBlendMode(BlendModeInfo model) throws IOException {
		
		model.setSignature(ByteUtil.readByteToStr(inputStream, 4));
		
		String key = ByteUtil.readByteToStr(inputStream, 4);
		model.setKey(key);
		
		BlendMode mode = BlendMode.bm(key);
		model.setMode(mode);
		return 4 + 4;
	}
	
	private int parseLayerSetFlags(LayerSetFlagInfo flag) throws IOException {
		int val = ByteUtil.readByteToInt(inputStream, 1);
		String binaryStr = Integer.toBinaryString(val);
		while(binaryStr.length() < 8) {
			binaryStr = "0" + binaryStr;
		}
		flag.setFlagStr(binaryStr);
		flag.setTransparencyProtected(Integer.parseInt(String.valueOf(binaryStr.charAt(7))));
		flag.setVisible(Integer.parseInt(String.valueOf(binaryStr.charAt(6))));
		flag.setObsolete(Integer.parseInt(String.valueOf(binaryStr.charAt(5))));
		flag.setUseful4(Integer.parseInt(String.valueOf(binaryStr.charAt(4))));
		flag.setDocumentPixData(Integer.parseInt(String.valueOf(binaryStr.charAt(3))));
		return 1;
	}
	
	private int parseLayerName(LayerNameInfo nameInfo) throws IOException {
		int nameLength = ByteUtil.readByteToInt(inputStream, 1);
		String name = "";
		int discardLength = 0;
		if(nameLength > 0) {
			name = ByteUtil.readByteToStr(inputStream, nameLength);
			if((1 + nameLength) % 4 != 0) {
				discardLength = 4 - ((1 + nameLength) % 4);
				
				byte[] bsDiscard = new byte[discardLength];
				inputStream.read(bsDiscard);
			}
		}
		nameInfo.setName(name);
		nameInfo.setNameLength(nameLength);
		nameInfo.setTotalSize(1 + nameLength + discardLength);
		return 1 + nameLength + discardLength;
	}
	
	private int parseLayerMaskData(LayerMaskData maskData) throws IOException {
		byte[] bsDataSize = new byte[4];
		inputStream.read(bsDataSize);
		int dataSize = ByteUtil.bytesToInt(bsDataSize);
		if(dataSize == 0) {
			maskData.setTotalDataSize(dataSize + 4);
			return 4;
		}
		byte[] bsData = new byte[dataSize];
		inputStream.read(bsData);
		
//		maskData.setCoordinate(parseLayerMaskCoordinate());
//		maskData.setDefaultColor(parseDefaultColor());
//		maskData.setFlagInfo(parseLayerMaskFlags());
//		
//		if(maskData.getFlagInfo().getMaskParameters() == 1) {
//			maskData.setMaskParameters(parseMaskParameters());
//		}else {
//			maskData.setMaskParameters(null);
//		}
		maskData.setTotalDataSize(dataSize + 4);
		return dataSize + 4;
	}
	
	private int parseLayerBlendingRange(LayerBlendingRange blendingRange) throws IOException {
		
		int layerBlendingRangeLength = ByteUtil.readByteToInt(inputStream, 4);
		int colorChannelNumber = (layerBlendingRangeLength - 8) / 8;
		
		blendingRange.setLayerBlendingRangeLength(layerBlendingRangeLength);
		blendingRange.setColorChannelNumber(colorChannelNumber);
		
		int compositeColorCurrentBlack = ByteUtil.readByteToInt(inputStream, 2);
		blendingRange.setCompositeColorCurrentBlack(compositeColorCurrentBlack);
		
		int compositeColorCurrentWhite = ByteUtil.readByteToInt(inputStream, 2);
		blendingRange.setCompositeColorCurrentWhite(compositeColorCurrentWhite);
		
		int compositeColorNextBlack = ByteUtil.readByteToInt(inputStream, 2);
		blendingRange.setCompositeColorNextBlack(compositeColorNextBlack);
		
		int compositeColorNextWhite = ByteUtil.readByteToInt(inputStream, 2);
		blendingRange.setCompositeColorNextWhite(compositeColorNextWhite);
		
		List<Map<String, Integer>> compositeColors = new ArrayList<>();
		for(int i = 0; i < colorChannelNumber; i ++) {
			int currentBlack = ByteUtil.readByteToInt(inputStream, 2);
			int currentWhite = ByteUtil.readByteToInt(inputStream, 2);
			int nextBlack = ByteUtil.readByteToInt(inputStream, 2);
			int nextWhite = ByteUtil.readByteToInt(inputStream, 2);
			
			Map<String, Integer> compositeColor = new HashMap<>();
			compositeColor.put("currentBlack", currentBlack);
			compositeColor.put("currentWhite", currentWhite);
			compositeColor.put("nextBlack", nextBlack);
			compositeColor.put("nextWhite", nextWhite);
			compositeColors.add(compositeColor);
		}
		blendingRange.setCompositeColors(compositeColors);
		blendingRange.setTotalDataSize(4 + layerBlendingRangeLength);
		return 4 + layerBlendingRangeLength;
	}
	
	private int parseLayerEffects(LayerRecord record) throws IOException {
		int extraLength = record.getExtraDataLength();
		int effectLength = extraLength
				- record.getMaskData().getTotalDataSize()
				- record.getBlendingRange().getTotalDataSize()
				- record.getNameInfo().getTotalSize();
		
		LayerEffectHandle effectHandle = new LayerEffectHandle();
		
		List<LayerEffect> effects = new ArrayList<LayerEffect>();
		
		int readLength = 0;
		while (readLength < effectLength) {
			LayerEffect effect = new LayerEffect();
			
			String sign = ByteUtil.readByteToStr(inputStream, 4);
			effect.setSignature(sign);
			readLength += 4;
			
			String label = ByteUtil.readByteToStr(inputStream, 4);
			effect.setLabel(label);
			readLength += 4;
			
			int length = ByteUtil.readByteToInt(inputStream, 4);
			readLength += 4;
			
			byte[] effectData = new byte[length];
			readLength += inputStream.read(effectData);
			
			EffectSet set = EffectSet.bm(label);
			effect.setEffectSet(set);
			Map<String, Object> effectInfo = effectHandle.handle(set, effectData);
			effect.setEffectInfo(effectInfo);
			
			effects.add(effect);
		}
		record.setEffects(effects);
		return readLength;
	}
}
